#pragma once

#include <sys/types.h>
#include <regex.h>
#include <string>
#include <limits>
#include <vector>
#include <memory>


namespace evm { namespace re {

using namespace std;

static const regoff_t NO_MATCH = -1;

static const int NONE = 1;
static const int IGNORECASE = 2;
static const int MUILTILINE = 4;
static const int DOTALL = (8 | MUILTILINE);

class MatchObject {
public:
    /* Returns whether matched. */
    operator bool() const {
        return matched_;
    }

    /* Returns the number of the matched subgroups, includes the whole match. */
    size_t count() const {
        return matches_.size();
    }

    /* Returns the subgroup string of the match at the specified index.
     * Default index 0 represents the whole match string. */
    string group(size_t index = 0);

    MatchObject(const MatchObject&) = default;
    MatchObject(MatchObject&&) noexcept = default;
    MatchObject& operator=(const MatchObject&) = default;
    MatchObject& operator=(MatchObject&&) = default;

private:
    MatchObject() {}
    MatchObject(const string& str) : str_(str) {}

    string str_;
    bool matched_{};
    vector<pair<regoff_t, regoff_t>> matches_;

    friend class RegexObject;
};

class RegexObject {
public:
    RegexObject(const string& pattern, int flags = MUILTILINE);

    ~RegexObject();

    MatchObject search(const string& str, size_t start = 0,
                       size_t end = numeric_limits<size_t>::max()) const;

    MatchObject match(const string& str, bool whole = false) const;

    vector<MatchObject> search_all(const string& str, size_t start = 0,
                                   size_t end = numeric_limits<size_t>::max()) const;

private:
    regex_t re_;
    int eflags_{};
    size_t nmatch_;
};

using RegexPtr = shared_ptr<RegexObject>;

inline RegexObject compile(const string& pattern, int flags = MUILTILINE) {
    return RegexObject(pattern, flags);
}

inline MatchObject search(const string& pattern, const string& str, int flags = MUILTILINE) {
    return compile(pattern, flags).search(str);
}

inline MatchObject match(const string& pattern, const string& str,
                         int flags = MUILTILINE, bool whole = false) {
    return compile(pattern, flags).match(str, whole);
}

}}
